/*
 * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.dtv.lwuit.plaf;

import com.sun.dtv.lwuit.geom.Dimension;
import com.sun.dtv.lwuit.*;
import com.sun.dtv.lwuit.animations.Transition;
import com.sun.dtv.lwuit.geom.Rectangle;

/**
 * Allows a UI developer to completely customize the look of the application by
 * overriding drawing/sizing methods appropriately.
 *
 * @author Chen Fishbein
 */
public abstract class LookAndFeel {
    /**
     * Tint color is set when a form is partially covered be it by a menu or by a 
     * dialog. A look and feel can override this default value.
     */
    private int defaultFormTintColor = 0x7f000000;
    
    /**
     * This color is used to paint disable mode.
     */
    private int disableColor = 0xcccccc;
    
    /**
     * This member allows us to define a default animation that will draw the transition for
     * entering a form
     */
    private Transition defaultFormTransitionIn;

    /**
     * This member allows us to define a default animation that will draw the transition for
     * exiting a form
     */
    private Transition defaultFormTransitionOut;

    /**
     * This member allows us to define a default animation that will draw the transition for
     * entering a menu
     */
    private Transition defaultMenuTransitionIn;

    /**
     * This member allows us to define a default animation that will draw the transition for
     * exiting a menu
     */
    private Transition defaultMenuTransitionOut;
    
    /**
     * This member allows us to define a default animation that will draw the transition for
     * entering a dialog
     */
    private Transition defaultDialogTransitionIn;

    /**
     * This member allows us to define a default animation that will draw the transition for
     * exiting a form
     */
    private Transition defaultDialogTransitionOut;

    /**
     * Indicates whether lists and containers should have smooth scrolling by default
     */
    private boolean defaultSmoothScrolling = true;
    
    /**
     * Indicates the default speed for smooth scrolling
     */
    private int defaultSmoothScrollingSpeed = 150;
    
    /**
     * Indicates whether softbuttons should be reversed from their default orientation
     */
    private boolean reverseSoftButtons;

    /**
     * Every component binds itself to the look and feel thus allowing the look 
     * and feel to customize the component.  Binding occurs at the end of the
     * constructor when the component is in a valid state and ready to be used.
     * Notice that a component might be bound twice or more and it is the 
     * responsibility of the LookAndFeel to protect against that.
     * 
     * @param cmp component instance that may be customized by the look and feel
     */
    public void bind(Component cmp) {
    }

    /**
     * Invoked when a look and feel is removed, allows a look and feel to release 
     * resources related to binding components.
     * 
     * @see #bind(Component)
     */
    public void uninstall() {}
    
    /**
     * Invoked for drawing a button widget
     */    
    public abstract void drawButton(Graphics g, Button b);
    
    /**
     * Invoked for drawing a checkbox widget
     */    
    public abstract void drawCheckBox(Graphics g, CheckBox cb);
    
    /**
     * Invoked for drawing a combo box widget
     */    
    public abstract void drawComboBox(Graphics g, ComboBox cb);
    
    /**
     * Invoked for drawing a label widget
     */    
    public abstract void drawLabel(Graphics g, Label l);
    
    /**
     * Invoked for drawing a list widget
     */    
    public abstract void drawList(Graphics g, List l);
    
    /**
     * Invoked for drawing a month view widget
     */    
    public abstract void drawMonthView(Graphics g, Calendar cal, Component mv);

    /**
     * Returns the day of the month in the month view at the given relative
     * component X/Y offsets. This is important for touch screen interaction
     */
    public abstract long findDayAt(int x, int y, Calendar cal, Component mv);
    
    /**
     * Invoked for drawing the radio button widget
     */    
    public abstract void drawRadioButton(Graphics g, RadioButton rb);
    
    
    public abstract void drawTextArea(Graphics g, TextArea ta);

    /**
     * Draws the text field without its cursor which is drawn in a separate method
     * input mode indication can also be drawn using this method.
     */
    public abstract void drawTextField(Graphics g, TextField ta);

    /**
     * Draws the cursor of the text field, blinking is handled simply by avoiding
     * a call to this method.
     */
    public abstract void drawTextFieldCursor(Graphics g, TextField ta);

    /**
     * Invoked for drawing the Tab Pane widget
     */
    public abstract void drawTabbedPane(Graphics g, TabbedPane tp);
    
    /**
     * @return the preferred size for the radio button
     */
    public abstract Dimension getButtonPreferredSize(Button b);
    
    /**
     * @return the preferred size for the checkbox
     */
    public abstract Dimension getCheckBoxPreferredSize(CheckBox cb);
    
    /**
     * @return the preferred size for the label
     */
    public abstract Dimension getLabelPreferredSize(Label l);
    
    /**
     * @return the preferred size for the list
     */
    public abstract Dimension getListPreferredSize(List l);
    
    /**
     * @return the preferred size for the month view
     */
    public abstract Dimension getMonthViewPreferredSize(Component mv);
    
    /**
     * @return the preferred size for the radio button
     */
    public abstract Dimension getRadioButtonPreferredSize(RadioButton rb);
    
    //public abstract Dimension getSpinnerPreferredSize(Spinner sp);
    
    /**
     * Returns the preferred size of the text area
     * 
     * @return the preferred size for the text area
     */
    public abstract Dimension getTextAreaPreferredSize(TextArea ta);

    /**
     * Returns the preferred size of the text field
     * 
     * @return the preferred size for the text field
     */
    public abstract Dimension getTextFieldPreferredSize(TextField ta);
    
    /**
     * @return the preferred size for the combo box
     */
    public abstract Dimension getComboBoxPreferredSize(ComboBox box);

    /**
     * Draws a vertical scoll bar in the given component
     */
    public void drawVerticalScroll(Graphics g, Component c, float offsetRatio, float blockSizeRatio) {
        int x = c.getX() + c.getWidth() - getVerticalScrollWidth();
        int y = c.getY();
        int height = c.getHeight();
        Style s = UIManager.getInstance().getComponentStyle("Scroll");
        Style st = UIManager.getInstance().getComponentStyle("ScrollThumb");

        g.setColor(s.getFgColor());
        
        int width = getVerticalScrollWidth();
        
        // take margin into consideration when positioning the scroll
        int marginLeft = s.getMargin(Component.LEFT);
        int marginTop = s.getMargin(Component.TOP);
        x += marginLeft;
        width -= (marginLeft + s.getMargin(Component.RIGHT));
        y += marginTop;
        height -= (marginTop + s.getMargin(Component.BOTTOM));
        
        if(s.getBgImage() != null) {
            drawBackgroundImage(g, s, x, y, width, height);
        } else {
            g.fillRect(x, y, width, height, s.getBgTransparency());
            g.setColor(s.getBgColor());

            g.fillRoundRect(x, y  + 1, width, height - 2, 10, 10);
        }        
        
        marginLeft = st.getMargin(Component.LEFT);
        marginTop = st.getMargin(Component.TOP);
        x += marginLeft;
        width -= (marginLeft + st.getMargin(Component.RIGHT));
        y += marginTop;
        height -= (marginTop + st.getMargin(Component.BOTTOM));

        int offset = (int)(height * offsetRatio);
        int blockSize = (int)(height * blockSizeRatio);
        if(st.getBgImage() != null) {
            drawBackgroundImage(g, st, x, y + offset ,width , blockSize);
        } else {
            g.setColor(st.getFgColor());
            g.fillRoundRect(x, y + offset , width, blockSize, 10, 10);
        }        
    }
    
    private void drawBackgroundImage(Graphics g, Style s, int x, int y, int width, int height) {
        Image bgImage = s.getBgImage();
        if(bgImage != null) {
            if (s.isScaleImage()) {
                if (bgImage.getWidth() != width || bgImage.getHeight() != height) {
                    bgImage = bgImage.scaled(width, height);
                }
                g.drawImage(bgImage, x, y);
            } else {
                int iW = bgImage.getWidth();
                int iH = bgImage.getHeight();
                for (int xPos = 0; xPos < width; xPos += iW) {
                    for (int yPos = 0; yPos < height; yPos += iH) {
                        g.drawImage(bgImage, x + xPos, y + yPos);
                    }
                }
            }
        }
    }

    /**
     * Draws a horizontal scoll bar in the given component
     */
    public void drawHorizontalScroll(Graphics g, Component c, float offsetRatio, float blockSizeRatio) {
        int x = c.getX();
        int y = c.getY() + c.getHeight() - getHorizontalScrollHeight();
        int width = c.getWidth();
        
        Style s = UIManager.getInstance().getComponentStyle("Scroll");

        g.setColor(s.getFgColor());
        
        int height = getHorizontalScrollHeight();
                  
        Painter p = s.getBgPainter();
        if(p != null) {
            p.paint(g, new Rectangle(x, y, width, height));
        } else {
            g.fillRect(x, y, width, height, s.getBgTransparency());
        }
        
        g.setColor(s.getBgColor());
            
        g.fillRoundRect(x + 1, y, width - 2, height, 10, 10);

        g.setColor(s.getFgColor());

        int offset = (int)(width * offsetRatio);
        int blockSize = (int)(width * blockSizeRatio);
        g.fillRoundRect(x + offset , y, blockSize , height, 10, 10);
    }
        
    
    /**
     * Sets the foreground color and font for a generic component, reuse-able by most component
     * drawing code
     */
    public void  setFG(Graphics g, Component c) {
        Style s = c.getStyle();
        g.setFont(s.getFont());
        if(c.isEnabled()) {
            if(c.hasFocus() && c.isFocusPainted()){
                g.setColor(s.getFgSelectionColor());
            }else{
                g.setColor(s.getFgColor());
            }
        } else {
            g.setColor(disableColor);
        }
    }
    
    /**
     * Returns the default width of a vertical scroll bar
     * 
     * @return default width of a vertical scroll bar
     */
    public int getVerticalScrollWidth() {
        Style s = UIManager.getInstance().getComponentStyle("Scroll");
        return s.getMargin(Component.LEFT) + s.getMargin(Component.RIGHT) + 
            s.getPadding(Component.LEFT) + s.getPadding(Component.RIGHT);
    }

    /**
     * Returns the default height of a horizontal scroll bar
     * 
     * @return default height of a horizontal scroll bar
     */
    public int getHorizontalScrollHeight() {
        Style s = UIManager.getInstance().getComponentStyle("Scroll");
        return s.getMargin(Component.TOP) + s.getMargin(Component.BOTTOM) + 
            s.getPadding(Component.TOP) + s.getPadding(Component.BOTTOM);
    }

    /**
     * Draws generic component border
     * 
     * @deprecated replaced by the Border class
     */
    public void drawBorder(Graphics g, Component c, int color, int borderWidth) {
        drawBorder(g, c, color, color, borderWidth);
    }
    
    /**
     * Draws generic component border
     * 
     * @deprecated replaced by the Border class
     */
    public void drawBorder(Graphics g, Component c, int topAndRightColor, int bottomAndLeftColor, int borderWidth) {
        g.setColor(topAndRightColor);     //Text Component upper border color
        g.fillRect(c.getX(), c.getY(), c.getWidth(),borderWidth);
        g.fillRect(c.getX(), c.getY(), borderWidth, c.getHeight());
        g.setColor(bottomAndLeftColor);     //Text Component lower border color
        g.fillRect(c.getX(), c.getY() + c.getHeight()-borderWidth, c.getWidth(), borderWidth);
        g.fillRect(c.getX() + c.getWidth()-borderWidth, c.getY() , borderWidth, c.getHeight());
    }
    
    /**
     * Draws and return the TabbedPane cell component (renderer)
     * according to each tab orientation, the borders are getting draws
     * 
     * @param tp the TabbedPane
     * @param text the cell text
     * @param icon the cell icon image
     * @param isSelected is the cell is the selected one
     * @param cellHasFocus is the cell has focus
     * @param cellStyle the cell Style object
     * @param tabbedPaneStyle the TabbedPane Style object
     * @param cellOffsetX the offset when the cell is on TOP or BOTTOM orientation
     * @param cellOffsetY the offset when the cell is on LEFT or RIGHT orientation
     * @param cellsPreferredSize the total cells PreferredSize
     * @param contentPaneSize the contentPaneSize
     * @return A TabbedPane cell component
     */
    public abstract Component getTabbedPaneCell(final TabbedPane tp,
            final String text, final Image icon, final boolean isSelected,
            final boolean cellHasFocus, final Style cellStyle,
            final Style tabbedPaneStyle, final int cellOffsetX ,
            final int cellOffsetY, final Dimension cellsPreferredSize, 
            final Dimension contentPaneSize);
    
    /**
     * Draws and return the TabbedPane contentpane painter
     * 
     * @param tp the TabbedPane
     * @param g the content pane graphics
     * @param rect the content pane painting rectangle area
     * @param cellsPreferredSize the total cells PreferredSize
     * @param numOfTabs number of tabs
     * @param selectedTabIndex the selected tab index
     * @param tabsSize the tabs size
     * @param cellOffsetX the offset when the cell is on TOP or BOTTOM orientation
     * @param cellOffsetY the offset when the cell is on LEFT or RIGHT orientation
     */
    public abstract void drawTabbedPaneContentPane(final TabbedPane tp,
                    final Graphics g, final Rectangle rect, 
                    final Dimension cellsPreferredSize, final int numOfTabs,
                    final int selectedTabIndex, final Dimension tabsSize,
                    final int cellOffsetX , final int cellOffsetY);

    /**
     * Allows us to define a default animation that will draw the transition for
     * entering a form
     */
    public Transition getDefaultFormTransitionIn() {
        return defaultFormTransitionIn;
    }

    /**
     * Allows us to define a default animation that will draw the transition for
     * entering a form
     */
    public void setDefaultFormTransitionIn(Transition defaultFormTransitionIn) {
        this.defaultFormTransitionIn = defaultFormTransitionIn;
    }

    /**
     * Allows us to define a default animation that will draw the transition for
     * exiting a form
     */
    public Transition getDefaultFormTransitionOut() {
        return defaultFormTransitionOut;
    }

    /**
     * Allows us to define a default animation that will draw the transition for
     * exiting a form
     */
    public void setDefaultFormTransitionOut(Transition defaultFormTransitionOut) {
        this.defaultFormTransitionOut = defaultFormTransitionOut;
    }

    /**
     * Allows us to define a default animation that will draw the transition for
     * entering a Menu
     */
    public Transition getDefaultMenuTransitionIn() {
        return defaultMenuTransitionIn;
    }

    /**
     * Allows us to define a default animation that will draw the transition for
     * entering a Menu
     */
    public void setDefaultMenuTransitionIn(Transition defaultMenuTransitionIn) {
        this.defaultMenuTransitionIn = defaultMenuTransitionIn;
    }

    /**
     * Allows us to define a default animation that will draw the transition for
     * exiting a Menu
     */
    public Transition getDefaultMenuTransitionOut() {
        return defaultMenuTransitionOut;
    }

    /**
     * Allows us to define a default animation that will draw the transition for
     * exiting a Menu
     */
    public void setDefaultMenuTransitionOut(Transition defaultMenuTransitionOut) {
        this.defaultMenuTransitionOut = defaultMenuTransitionOut;
    }

    /**
     * Allows us to define a default animation that will draw the transition for
     * entering a dialog
     */
    public Transition getDefaultDialogTransitionIn() {
        return defaultDialogTransitionIn;
    }

    /**
     * Allows us to define a default animation that will draw the transition for
     * entering a dialog
     */
    public void setDefaultDialogTransitionIn(Transition defaultDialogTransitionIn) {
        this.defaultDialogTransitionIn = defaultDialogTransitionIn;
    }

    /**
     * Allows us to define a default animation that will draw the transition for
     * exiting a dialog
     */
    public Transition getDefaultDialogTransitionOut() {
        return defaultDialogTransitionOut;
    }

    /**
     * Allows us to define a default animation that will draw the transition for
     * exiting a dialog
     */
    public void setDefaultDialogTransitionOut(Transition defaultDialogTransitionOut) {
        this.defaultDialogTransitionOut = defaultDialogTransitionOut;
    }

    /**
     * Tint color is set when a form is partially covered be it by a menu or by a 
     * dialog. A look and feel can override this default value.
     */
    public int getDefaultFormTintColor() {
        return defaultFormTintColor;
    }

    /**
     * Tint color is set when a form is partially covered be it by a menu or by a 
     * dialog. A look and feel can override this default value.
     */
    public void setDefaultFormTintColor(int defaultFormTintColor) {
        this.defaultFormTintColor = defaultFormTintColor;
    }

    /**
     * This color is used to paint disable mode text color.
     
     * @return the color value
     */
    public int getDisableColor() {
        return disableColor;
    }

    /**
     * Simple setter to diable color
     * 
     * @param disableColor the diable color value
     */
    public void setDisableColor(int disableColor) {
        this.disableColor = disableColor;
    }

    
    /**
     * Indicates whether lists and containers should have smooth scrolling by default
     */
    public boolean isDefaultSmoothScrolling() {
        return defaultSmoothScrolling;
    }

    /**
     * Indicates whether lists and containers should have smooth scrolling by default
     */
    public void setDefaultSmoothScrolling(boolean defaultSmoothScrolling) {
        this.defaultSmoothScrolling = defaultSmoothScrolling;
    }

    /**
     * Indicates the default speed for smooth scrolling
     */
    public int getDefaultSmoothScrollingSpeed() {
        return defaultSmoothScrollingSpeed;
    }

    /**
     * Indicates the default speed for smooth scrolling
     */
    public void setDefaultSmoothScrollingSpeed(int defaultSmoothScrollingSpeed) {
        this.defaultSmoothScrollingSpeed = defaultSmoothScrollingSpeed;
    }

    /**
     * Indicates whether softbuttons should be reversed from their default orientation
     */
    public boolean isReverseSoftButtons() {
        return reverseSoftButtons;
    }

    /**
     * Indicates whether softbuttons should be reversed from their default orientation
     */
    public void setReverseSoftButtons(boolean reverseSoftButtons) {
        this.reverseSoftButtons = reverseSoftButtons;
    }
}
